{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Copyright 2021 Xilinx, Inc.\n",
    "#\n",
    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "#     http://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Example usages of Versal DDRMC 2D Eye Margin Scan"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setting up Environments"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import os\n",
    "import pprint\n",
    "\n",
    "#Windows\n",
    "CSPY_SRC = r\"C:\\Users\\ayang\\venvs\\chipscopy\\Lib\\site-packages\"\n",
    "\n",
    "#Linux\n",
    "#HOME = os.environ['HOME']\n",
    "#CSPY_SRC = f'{HOME}/.local/lib/python3.8/site-packages'\n",
    "#sys.path.append(CSPY_SRC)\n",
    "#print(sys.path)\n",
    "\n",
    "# import chipscopy modules\n",
    "from chipscopy import create_session, null_callback\n",
    "from chipscopy import report_versions\n",
    "from scan_util import convert_vref_pct_to_code\n",
    "from scan_util import get_write_vref_range\n",
    "\n",
    "# set up CS server URL\n",
    "CS_URL = \"TCP:xsjayang42x:3042\"\n",
    "# set up HW server URL\n",
    "HW_URL = \"TCP:morel11:3121\"\n",
    "# set up path to PDI file\n",
    "#PDI_FILE = f\"/group/siapps/ayang/projects/Everest/2d/designs/tenzing/verified/v3_ddr4_c0_udimm.pdi\"\n",
    "#single DDR4 below at index 0\n",
    "#PDI_FILE = f\"y:/ayang/projects/Everest/2d/designs/tenzing/verified/v3_ddr4_c0_udimm.pdi\"\n",
    "#LP4 below , at index 1 and 3\n",
    "PDI_FILE = f\"y:/ayang/projects/Everest/2d/designs/vck190/vck190.pdi\"\n",
    "# Which Adaptive SoC device in the debug chain\n",
    "acap_INDEX = 0\n",
    "# Which DDRMC target (0..3) for given acap\n",
    "DDR_INDEX = 1\n",
    "# Read or Write Margin : \"READ\" \"WRITE\"\n",
    "MARGIN_MODE = \"READ\"\n",
    "# Data pattern used for margin check : \"SIMPLE\" \"COMPLEX\"\n",
    "DATA_PATTERN = \"COMPLEX\"\n",
    "# VREF Percentage Minimum (reccommended: Read :DDR4 25, LP4 5 , Write : DDR4 60  , LP4 10)\n",
    "VREF_PCT_MIN = 10\n",
    "# VREF Percentage Maximum (reccommended: Read:DDR4 50 , LP4 35 , Write : DDR4 90  , LP4 30)\n",
    "VREF_PCT_MAX = 30\n",
    "# Steps to show in the 2D eye scan  ( 1 step takes ~1 second to capture)\n",
    "STEPS = 15\n",
    "# Which nibble (read mode) or byte lane (write) to display\n",
    "DISPLAY_INDEX = 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## Establish hardware session"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "session = create_session(cs_server_url=CS_URL, hw_server_url=HW_URL, bypass_version_check=True)\n",
    "report_versions(session)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get the Versal device found"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if len(session.devices) == 0:\n",
    "    print('\\nNo devices detected')\n",
    "else:\n",
    "    versal_device = session.devices[acap_INDEX]\n",
    "    print(f\"Versal device found at device index number {acap_INDEX}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Programming the device"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f\"Programming {PDI_FILE}...\")\n",
    "versal_device.program(PDI_FILE)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup Debug Cores after programming done"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f\"Discovering debug cores...\")\n",
    "versal_device.discover_and_setup_cores()\n",
    "print('DONE')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get a list of the integrated DDR Memory Controllers  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "ddr_list = versal_device.ddrs\n",
    "print(f\"{len(ddr_list)} integrated DDRMC cores exist on this device.\")\n",
    "ddr_index=0\n",
    "for ddr in ddr_list:\n",
    "    if ddr.is_enabled:\n",
    "      print(f\" DDRMC instance {ddr_index} is enabled\" )\n",
    "    else : \n",
    "      print(f\" DDRMC instance {ddr_index} is disabled\")\n",
    "    ddr_index += 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Select a target DDR by index and display calibration status"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ddr = ddr_list[DDR_INDEX]\n",
    "try: \n",
    "    props = ddr.ddr_node.get_property(['cal_status'])\n",
    "    print(f\"Calibration status of DDRMC instance {DDR_INDEX} is {props['cal_status']}\")\n",
    "except: \n",
    "    print(f\"The DDR controller at index {DDR_INDEX} is not in use\")    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Initialize the Margin Check feature in the DDRMC\n",
    "## These following 4 commands are obsolete in 2021.2 and later"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ddr.ddr_node.set_property({'mgchk_enable': 1})\n",
    "ddr.ddr_node.commit_property_group([])\n",
    "ddr.ddr_node.set_property({'mgchk_enable': 0})\n",
    "ddr.ddr_node.commit_property_group([])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Applying 2D eye scan settings\n",
    "## Setting the 2D eye scan read or write mode"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if  MARGIN_MODE == \"READ\" :\n",
    "    print('Setting 2D eye for READ margin')\n",
    "    ddr.set_eye_scan_read_mode()\n",
    "elif MARGIN_MODE == \"WRITE\" :\n",
    "    print('Setting 2D eye for WRITE margin')\n",
    "    ddr.set_eye_scan_write_mode()\n",
    "else :\n",
    "    print(f\" ERROR: MARGIN_MODE is set to {MARGIN_MODE} which is an illegal value, only READ or WRITE is allowed\")\n",
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Setting the 2D eye scan data pattern mode"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if  DATA_PATTERN == \"SIMPLE\" :\n",
    "    print('Setting 2D eye for SIMPLE data pattern')\n",
    "    ddr.set_eye_scan_simple_pattern()\n",
    "elif DATA_PATTERN == \"COMPLEX\" :\n",
    "    print('Setting 2D eye for COMPLEX data pattern')\n",
    "    ddr.set_eye_scan_complex_pattern()\n",
    "else :\n",
    "    print(f\" ERROR: DATA_PATTERN is set to {DATA_PATTERN} which is an illegal value, only SIMPLE or COMPLEX is allowed\")\n",
    " "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setting the Vref sample min/max range"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('Vref Min setting...')\n",
    "vref_min_code = convert_vref_pct_to_code(ddr, MARGIN_MODE, VREF_PCT_MIN)\n",
    "print('Vref Max setting...')\n",
    "vref_max_code = convert_vref_pct_to_code(ddr, MARGIN_MODE, VREF_PCT_MAX)\n",
    "\n",
    "ddr.set_eye_scan_vref_min(vref_min_code)\n",
    "ddr.set_eye_scan_vref_max(vref_max_code)\n",
    "ddr.set_eye_scan_vref_steps(STEPS)\n",
    "print(f\"Dividing the Vref range into {STEPS} steps\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run 2D Margin Scan after settings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ddr.run_eye_scan()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Display Scan Plots by a given Unit(nibble/byte) index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "ddr.display_eye_scan(DISPLAY_INDEX)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Optionally you can also save the figures as a list for later operations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "figs = ddr.display_eye_scan(DISPLAY_INDEX, True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following demonstrates that you can display the graphs from a list created previously"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# for fig in figs:\n",
    "#    fig.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Save the Eye Scan data from latest run"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ddr.save_eye_scan_data('myoutput.csv')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load Eye Scan data from a given data file"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ddr.load_eye_scan_data('myoutput.csv')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Review overall Scan status from latest run"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# props = ddr.ddr_node.get_property_group(['eye_scan_stat'])\n",
    "# print(pprint.pformat(props, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## You can also run a report on the DDR to see a full config and calibration/margin Info"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ddr.report()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "## When done with testing, close the cs_server connection \n",
    "# session.cs_server.close()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
